﻿/*
Copyright (c) 2012 Maksim Gordeev

Permission is hereby granted, free of charge, to any person obtaining a copy of this
software and associated documentation files (the "Software"), to deal in the Software
without restriction, including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

 */

/*
 * Сделано в SharpDevelop.
 * Пользователь: maxxadm
 * Дата: 13.07.2012
 * Время: 17:37
 * 
 * Для изменения этого шаблона используйте Сервис | Настройка | Кодирование | Правка стандартных заголовков.
 */
using System;
using System.IO;
using System.Threading;
using System.ComponentModel;
using Hammock;
using Hammock.Authentication.OAuth;
using System.Collections.Concurrent;
using Newtonsoft.Json;
using System.Windows.Threading;

namespace jellybird.core
{
	public sealed class TwAccount:INotifyPropertyChanged
	{
		
		
		public TwAccount
			()
		{
			
		}
		
		#region INotify implementation
		public event PropertyChangedEventHandler PropertyChanged;
		
		private void On_property_changed(string prop)
		{
			if(PropertyChanged!=null)
			{
				PropertyChanged.Invoke(this,new PropertyChangedEventArgs(prop));
			}
		}
		#endregion
		
		public event EventHandler<VerifyFailedEventArgs> VerifyFailed;
		
		
		
		private object sync_root;
		private const int TIMEOUT=10;
		private TwCredentials m_credentials;
		private TwAccountSettings m_account_settings;
		private bool m_auth_success;
		private long m_user_id;
		
		private void On_verify_failed(System.Net.HttpStatusCode status,Exception exception)
		{
			if(VerifyFailed!=null)
			{
				Dispatcher dispatcher=System.Windows.Application.Current.Dispatcher;
				if(dispatcher.CheckAccess())
				{
					VerifyFailed.Invoke(this,new VerifyFailedEventArgs(status,exception));
				}
				else
				{
					Delegate[] handlers=VerifyFailed.GetInvocationList();
					foreach(var handler in handlers)
					{
						dispatcher.BeginInvoke(handler,this,new VerifyFailedEventArgs(status,exception));
					}
				}
			}
		}
		
		private object SyncRoot
		{
			get
			{
				
				
				if(sync_root==null)
				{
					object temp_root=new object();
					Interlocked.Exchange(ref sync_root,temp_root);
				}
				
				return sync_root;
			}
		}
		
		public TwCredentials Credentials
		{
			get
			{
				TwCredentials ret=null;
				lock(SyncRoot)
				{
					ret=m_credentials;
				}
				return ret;
			}
			set
			{
				lock(SyncRoot)
				{
					m_credentials=value;
				}
				AuthSuccess=false;
			}
		}
		
		
		
		private TwTimeline m_Current;
		public TwTimeline Current
		{
			get
			{
				return m_Current;
			}
			set
			{
				m_Current=value;
				On_property_changed("Current");
			}
		}
		
		public TwTimeline HomeTimeline
		{
			get
			{
				return TwTimeline.Get(Credentials,TimelineKind.Home);
			}
		}
		
		public TwTimeline UserTimeline
		{
			get
			{
				return TwTimeline.Get(Credentials,TimelineKind.User);
			}
		}
		
		public TwTimeline MentionsTimeline
		{
			get
			{
				return TwTimeline.Get(Credentials,TimelineKind.Mentions);
			}
		}
		
		public long UserId
		{
			get
			{
				long ret=0;
				lock(SyncRoot)
				{
					ret=m_user_id;
				}
				return ret;
			}
			private set
			{
				lock(SyncRoot)
				{
					m_user_id=value;
				}
			}
		}
		
		public TwAccountSettings AccountSetings
		{
			get
			{
				if(m_account_settings==null)
				{
					TwAccountSettings ret=get_account_settings_internal();
					Interlocked.CompareExchange(ref m_account_settings,ret,null);
				}
				
				return m_account_settings;
			}
			set
			{
				lock(SyncRoot)
				{
					m_account_settings=value;
				}
				On_property_changed("AccountSettings");
			}
		}
		
		public TwUser AccountUser
		{
			get
			{
				if(UserId==0)
					return null;
				
				if(Credentials==null)
					return null;
				
				return TwUsersCache.Get(Credentials)[UserId];
			}
		}
		
		public bool AuthSuccess
		{
			get
			{
				bool ret=false;
				lock(SyncRoot)
				{
					ret=m_auth_success;
				}
				return ret;
			}
			private set
			{
				lock(SyncRoot)
				{
					m_auth_success=value;
				}
				On_property_changed("AuthSuccess");
			}
		}
		
		public void BeginVerify()
		{
			if ((Credentials == null) || (!Credentials.IsProperlyFilled))
			{
				throw new ArgumentException("Credentials must be specifyied.");
			}

			begin_verify();
		}
		
		public bool Verify()
		{
			if ((Credentials == null) || (!Credentials.IsProperlyFilled))
			{
				throw new ArgumentException("Credentials must be specifyied.");
			}
			
			return verify_sync();
		}
		
		private TwAccountSettings get_account_settings_internal()
		{
			if(!AuthSuccess)
			{
				throw new InvalidOperationException("Cannot retrieve user settings while auth failed.");
			}
			
			TwUser user_object=this.AccountUser;
			if((user_object==null)||(user_object.Client==null))
			{
				throw new InvalidOperationException("There are strange user object.");
			}
			
			RestRequest request=new RestRequest()
			{
				Method=Hammock.Web.WebMethod.Get,
				Path=Constants.ACCOUNT+'/'+Constants.SETTINGS
			};
			
			RestResponse<TwAccountSettings> response=user_object.Client.Request<TwAccountSettings>(request);
			
			#if DEBUG
			Helper.OnDebugMessage
				(string.Format
				 ("Get user settings done. Uri='{1}' Status={0} Exception={2}",
				  response.StatusCode,
				  response.RequestUri,
				  response.InnerException==null?string.Empty:response.InnerException.Message));
			#endif
			
			TwAccountSettings ret=null;
			if(response.StatusCode==System.Net.HttpStatusCode.OK)
			{
				ret=response.ContentEntity;
			}
			response.Dispose();
			return ret;
		}
		
		private bool verify_sync()
		{
			RestClient rest_client=new RestClient()
			{
				Authority=Constants.API_TWITTER_COM,
				Credentials=new Hammock.Authentication.OAuth.OAuthCredentials()
				{
					ConsumerKey=Credentials.ConsumerKey,
					ConsumerSecret=Credentials.ConsumerSecret,
					SignatureMethod=Hammock.Authentication.OAuth.OAuthSignatureMethod.HmacSha1,
					Token=Credentials.AccessToken,
					TokenSecret=Credentials.AccessTokenSecret,
					Type=Hammock.Authentication.OAuth.OAuthType.AccessToken
				},
				Deserializer=new JsonDeserializerWrapper(),
				VersionPath=Constants.API_VERSION_1_1
			};
			
			RestRequest request=new RestRequest()
			{
				Method=Hammock.Web.WebMethod.Get,
				Path=Constants.ACCOUNT+'/'+Constants.VERIFY_CREDENTIALS
			};
			request.AddParameter(Constants.SKIP_STATUS,Constants.TRUE_STRING);
			
			RestResponse<TwUser> response=rest_client.Request<TwUser>(request);
			if(response.StatusCode!=System.Net.HttpStatusCode.OK)
			{
				AuthSuccess=false;
				#if DEBUG
				Helper.OnDebugMessage
					(string.Format
					 ("Verify failed. Request='{1}' Status={0} Exception={2}",
					  response.StatusCode,
					  response.RequestUri,
					  response.InnerException==null?string.Empty:response.InnerException.Message));
				#endif
				/*
				 * Здесь можно и событие поднять чтоб известить клиента
				 */
				
				On_verify_failed(response.StatusCode,response.InnerException);
				response.Dispose();
				return false;
			}
			
			TwUser received=response.ContentEntity;
			if(received==null)
			{
				AuthSuccess=false;
				#if DEBUG
				Helper.OnDebugMessage
					(string.Format
					 ("Deserialize failed. Request='{1}' Status={0}",
					  response.StatusCode,
					  response.RequestUri));
				#endif
				/*
				 * Здесь можно и событие поднять чтоб известить клиента
				 */
				On_verify_failed(response.StatusCode,response.InnerException);
				response.Dispose();
				return false;
			}
			
			try
			{
				received.Client=rest_client;
				received.Credentials=Credentials;
				
				#if DEBUG
				Helper.OnDebugMessage
					(string.Format
					 ("Verify success. Request='{1}' Status={0} ScreenName={2} Id={3}",
					  response.StatusCode,
					  response.RequestUri,
					  received.ScreenName,
					  received.Id));
				#endif
				
				//в кэш пихаем
				TwUsersCache.Get(Credentials)[received.Id]=received;
				UserId=received.Id;
				AuthSuccess=true;
				On_property_changed("AccountUser");
				return true;
			}
			finally
			{
				if(response!=null)
					response.Dispose();
			}
		}
		
		private void begin_verify()
		{
			/*
			 * https://dev.twitter.com/docs/api/1.1/get/account/verify_credentials
			 */
			RestClient rest_client=new RestClient()
			{
				Authority=Constants.API_TWITTER_COM,
				Credentials=new Hammock.Authentication.OAuth.OAuthCredentials()
				{
					ConsumerKey=Credentials.ConsumerKey,
					ConsumerSecret=Credentials.ConsumerSecret,
					SignatureMethod=Hammock.Authentication.OAuth.OAuthSignatureMethod.HmacSha1,
					Token=Credentials.AccessToken,
					TokenSecret=Credentials.AccessTokenSecret,
					Type=Hammock.Authentication.OAuth.OAuthType.AccessToken
				},
				Deserializer=new JsonDeserializerWrapper(),
				VersionPath=Constants.API_VERSION_1_1
			};
			
			RestRequest request=new RestRequest()
			{
				Method=Hammock.Web.WebMethod.Get,
				Path=Constants.ACCOUNT+'/'+Constants.VERIFY_CREDENTIALS
			};
			request.AddParameter(Constants.SKIP_STATUS,Constants.TRUE_STRING);
			
			IAsyncResult res=rest_client.BeginRequest<TwUser>
				(request,
				 new RestCallback<TwUser>(verify_response),
				 rest_client);
		}
		
		private void verify_response
			(RestRequest request,
			 RestResponse<TwUser> response,
			 object user_state)
		{
			if(response.StatusCode!=System.Net.HttpStatusCode.OK)
			{
				//auth failed
				AuthSuccess=false;
				
				On_verify_failed(response.StatusCode,response.InnerException);
				
				response.Dispose();
				return;
			}
			
			TwUser received=response.ContentEntity;
			if(received==null)
			{
				AuthSuccess=false;
				
				On_verify_failed(response.StatusCode,response.InnerException);
				
				response.Dispose();
				return;
			}
			
			RestClient rest_client=user_state as RestClient;
			
			try
			{
				received.Client=rest_client;
				received.Credentials=Credentials;
				
				//в кэш пихаем
				TwUsersCache.Get(Credentials)[received.Id]=received;
				UserId=received.Id;
				AuthSuccess=true;
				On_property_changed("AccountUser");
			}
			finally
			{
				if(response!=null)
					response.Dispose();
			}
			
			
		}
		
		private void account_settings_response
			(RestRequest request,
			 RestResponse<TwAccountSettings> response,
			 object user_state)
		{
			try
			{
				if(response.StatusCode==System.Net.HttpStatusCode.OK)
				{
					AccountSetings=response.ContentEntity;
				}
			}
			finally
			{
				if(response!=null)
					response.Dispose();
			}
		}
		
	}
	
	public class VerifyFailedEventArgs:EventArgs
	{
		public VerifyFailedEventArgs(System.Net.HttpStatusCode status,Exception exception)
		{
			Status=status;
			Exception=exception;
		}
		
		public Exception Exception { get; private set; }
		public System.Net.HttpStatusCode Status { get; private set; }
	}
}
